package com.lishui.waterojbackendjudgeservice.judge;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lishui.waterojbackendcommon.contant.MessageConstant;
import com.lishui.waterojbackendcommon.exception.BusinessException;
import com.lishui.waterojbackendjudgeservice.judge.codesandbox.CodeSandbox;
import com.lishui.waterojbackendjudgeservice.judge.codesandbox.CodeSandboxFactory;
import com.lishui.waterojbackendjudgeservice.judge.codesandbox.CodeSandboxProxy;
import com.lishui.waterojbackendjudgeservice.judge.strategy.JudgeContext;
import com.lishui.waterojbackendmodel.model.codesandbox.ExecuteCodeRequest;
import com.lishui.waterojbackendmodel.model.codesandbox.ExecuteCodeResponse;
import com.lishui.waterojbackendmodel.model.enumeration.JudgeInfoMessageEnum;
import com.lishui.waterojbackendmodel.model.enumeration.QuestionSubmitStatusEnum;
import com.lishui.waterojbackendmodel.model.pojo.entity.JudgeCase;
import com.lishui.waterojbackendmodel.model.pojo.entity.JudgeInfo;
import com.lishui.waterojbackendmodel.model.pojo.entity.Question;
import com.lishui.waterojbackendmodel.model.pojo.entity.QuestionSubmit;
import com.lishui.waterojbackendserviceclient.service.QuestionFeignClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
public class JudgeServiceImpl implements JudgeService {

    @Resource
    private QuestionFeignClient questionFeignClient;

    @Resource
    private JudgeManager judgeManager;

    @Value("${codesandbox.type:example}")
    private String type;


    @Override
    @Transactional
    public QuestionSubmit doJudge(long questionSubmitId) {
        // 1）传入题目的提交 id，获取到对应的题目、提交信息（包含代码、编程语言等）
        QuestionSubmit questionSubmit = questionFeignClient.getQuestionSubmitById(questionSubmitId);
        if (questionSubmit == null) {
            throw new BusinessException(MessageConstant.NO_EXISTS + ": 提交信息");
        }
        Long questionId = questionSubmit.getQuestionId();
        Question question = questionFeignClient.getQuestionById(questionId);
        if (question == null) {
            throw new BusinessException(MessageConstant.NO_EXISTS + ": 题目");
        }
        // 2）如果题目提交状态不为等待中，就不用重复执行了
        if (!questionSubmit.getStatus().equals(QuestionSubmitStatusEnum.WAITING.getValue())) {
            throw new BusinessException("题目正在判题中");
        }
        // 3）更改判题（题目提交记录）的状态为 “判题中”，防止重复执行
        QuestionSubmit questionSubmitUpdate = new QuestionSubmit();
        questionSubmitUpdate.setId(questionSubmitId);
        questionSubmitUpdate.setStatus(QuestionSubmitStatusEnum.RUNNING.getValue());
        boolean update = questionFeignClient.updateQuestionSubmitById(questionSubmitUpdate);
        if (!update) {
            throw new BusinessException(MessageConstant.UPDATE_EXCEPTION + ": 题目状态");
        }
        // 4）调用沙箱，获取到执行结果
        CodeSandbox codeSandbox = CodeSandboxFactory.newInstance(type);
        codeSandbox = new CodeSandboxProxy(codeSandbox);
        String language = questionSubmit.getLanguage();
        String code = questionSubmit.getCode();
        // 获取输入用例
        String judgeCaseStr = question.getJudgeCase();
        List<JudgeCase> judgeCaseList = JSONUtil.toList(judgeCaseStr, JudgeCase.class);
        List<String> inputList = judgeCaseList.stream().map(JudgeCase::getInput).collect(Collectors.toList());
        ExecuteCodeRequest executeCodeRequest = ExecuteCodeRequest.builder()
                .code(code)
                .language(language)
                .inputList(inputList)
                .build();
        ExecuteCodeResponse executeCodeResponse = codeSandbox.executeCode(executeCodeRequest);
        // 5）根据沙箱的执行结果，设置题目的判题状态和信息
        JudgeInfo judgeInfo = null;
        Integer executeCodeResponseStatus = executeCodeResponse.getStatus();
        if (!Objects.equals(executeCodeResponseStatus, JudgeInfoMessageEnum.ACCEPTED.getValue())) {
            // 编译或执行，发生错误
            judgeInfo = executeCodeResponse.getJudgeInfo();
        } else {
            // 正常执行
            List<String> outputList = executeCodeResponse.getOutputList();
            JudgeContext judgeContext = new JudgeContext();
            judgeContext.setJudgeInfo(executeCodeResponse.getJudgeInfo());
            judgeContext.setInputList(inputList);
            judgeContext.setOutputList(outputList);
            judgeContext.setJudgeCaseList(judgeCaseList);
            judgeContext.setQuestion(question);
            judgeContext.setQuestionSubmit(questionSubmit);
            // 选择判题代理进行判题
            judgeInfo = judgeManager.doJudge(judgeContext);
        }
        // 6）修改数据库中的判题结果
        questionSubmitUpdate = new QuestionSubmit();
        questionSubmitUpdate.setId(questionSubmitId);
        if (Objects.equals(executeCodeResponseStatus, JudgeInfoMessageEnum.SYSTEM_ERROR.getValue())) {
            questionSubmitUpdate.setStatus(QuestionSubmitStatusEnum.FAILED.getValue());
        } else {
            questionSubmitUpdate.setStatus(QuestionSubmitStatusEnum.SUCCEED.getValue());
        }
        questionSubmitUpdate.setJudgeInfo(JSONUtil.toJsonStr(judgeInfo));
        update = questionFeignClient.updateQuestionSubmitById(questionSubmitUpdate);
        if (!update) {
            throw new BusinessException(MessageConstant.UPDATE_EXCEPTION + ": 题目状态");
        }
        // 7). 统计题目提交数和通过数
        Question questionUpdateNum = new Question();
        questionUpdateNum.setId(questionId);
        QuestionSubmit questionSubmitCount = new QuestionSubmit();
        questionSubmitCount.setQuestionId(questionId);
        Long submitNumCount = questionFeignClient.count(questionSubmitCount);
        questionSubmitCount.setJudgeInfo("Accepted");
        Long acceptNumCount = questionFeignClient.count(questionSubmitCount);
        questionUpdateNum.setSubmitNum(submitNumCount.intValue());
        questionUpdateNum.setAcceptedNum(acceptNumCount.intValue());
        boolean updateNum = questionFeignClient.updateQuestionById(questionUpdateNum);
        if (!updateNum) throw new BusinessException(MessageConstant.UPDATE_EXCEPTION + ": 统计参数");
        return questionFeignClient.getQuestionSubmitById(questionSubmitId);
    }
}
